import React from 'react';
import { Card, Typography, List, Row, Col } from 'antd';
import MarkdownCodeBlock from '@/components/MarkdownCodeBlock';
// @ts-ignore
import getFileTypeByExtension from '!!raw-loader!@/utils/file/getFileTypeByExtension';
// @ts-ignore
import getFileTypeByFileType from '!!raw-loader!@/utils/file/getFileTypeByFileType';
// @ts-ignore
import getFileTypeByMagicNumber from '!!raw-loader!@/utils/file/getFileTypeByMagicNumber';
// @ts-ignore
import fileTypeAttack from '!!raw-loader!./fileTypeAttack.html';
// @ts-ignore
import fileTypeBackendAttack from '!!raw-loader!./fileTypeBackendAttack.html';
// @ts-ignore
import fileTypeAttackJS from '!!raw-loader!./fileTypeAttack.ts';
import './index.less'; // 引入样式文件

const { Title, Paragraph } = Typography;

const App: React.FC = () => {

  return (
    <div className="app">
      <Card className="main-card">
        <Title level={2}>获取文件类型的不同方式</Title>
        <Paragraph>
          在上传文件时，我们通常需要判断文件的类型，以确保文件的安全性和正确性。这里介绍了几种常见的方式来获取文件类型，并分析它们的优缺点。
        </Paragraph>

        <Row gutter={16}>
          <Col span={24}>
            <Card title="方式一：通过文件的后缀名获取文件类型">
              <MarkdownCodeBlock code={getFileTypeByExtension.toString()} />
              <Title level={5} style={{marginTop: '20px'}}>优点</Title>
              <ul>
                <li>兼容性好</li>
              </ul>
              <Title level={5}>缺点</Title>
              <ul>
                <li>这种方式依赖文件的后缀名来判断文件类型。然而，用户可以轻易更改文件的后缀名，导致文件类型判断错误。</li>
              </ul>
            </Card>
          </Col>
        </Row>

        <Row gutter={16}>
          <Col span={24}>
            <Card title="方式二：通过 file.type 获取文件类型">
              <MarkdownCodeBlock code={getFileTypeByFileType.toString()} />
              <Title level={5} style={{marginTop: '20px'}}>优点</Title>
              <ul>
                <li>实现简单</li>
                <li>这种方式使用 <code>file.type</code> 来判断文件类型。即使修改了文件的后缀名，<code>file.type</code> 仍然反映文件的实际类型。</li>
              </ul>
              <Title level={5}>缺点</Title>
              <ul>
                <li>这种方式不完全可靠，因为用户仍然可以伪造文件类型。</li>
              </ul>
              <div style={{marginBottom: 20}}>如果是前端验证，可以这样绕过：比如原本有一个上传功能， 通过file.type验证， 上传gif图片肯定不会通过</div>
              <MarkdownCodeBlock code={fileTypeAttack.toString()} />
              <div style={{marginTop: 20, marginBottom: 20}}>然后注入如下脚本，上传gif图片就可以了</div>
              <MarkdownCodeBlock code={fileTypeAttackJS.toString()} />
              <div style={{marginTop: 20, marginBottom: 20}}>如果是后端验证，可以这样绕过：上传一个exe文件的文件，然后主动改为image/png的形式</div>
              <MarkdownCodeBlock code={fileTypeBackendAttack.toString()} />
            </Card>
          </Col>
        </Row>

        <Row gutter={16}>
          <Col span={24}>
            <Card title="方式三：使用文件魔术签名（Magic Number）">
              <MarkdownCodeBlock code={getFileTypeByMagicNumber.toString()} />
              <Title level={5} style={{marginTop: '20px'}}>优点</Title>
              <ul>
                <li>这种方式通过读取文件的魔术签名来判断文件类型。魔术签名是文件的开头字节序列，通常是特定文件格式的标识。</li>
                <li>即使用户修改了文件的后缀名或伪造了文件类型，也很难通过这种方式欺骗系统。</li>
              </ul>
              <Title level={5}>缺点</Title>
              <ul>
                <li>需要额外的处理步骤，可能会略微增加性能开销，但这种方式是最为可靠的。</li>
              </ul>
            </Card>
          </Col>
        </Row>

        <Row gutter={16}>
          <Col span={24}>
            <Card title="总结">
              <List
                bordered
                dataSource={[
                  '方式一（后缀名）不可靠，因为用户可以轻松更改后缀名。',
                  '方式二（<code>file.type</code>）比后缀名稍微靠谱，但仍然可以被伪造。',
                  '方式三（魔术签名）是最为可靠的方式，因为它直接读取文件内容进行判断，不容易被伪造。',
                ]}
                renderItem={item => <List.Item>{item}</List.Item>}
              />
              <Title level={5} style={{marginTop: '20px'}}>推荐：使用魔术签名来判断文件类型，确保文件安全。</Title>
            </Card>
          </Col>
        </Row>
      </Card>
    </div>
  )
}

export default App;
